﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using DCT.Model;
using System.Xml.Linq;
using System.Reflection;
using System.Windows;

namespace DCT.ViewModel
{
    public class DeviceViewModelRepository
    {
        #region Constructor

        public DeviceViewModelRepository(DeviceMapViewModel deviceMapViewModel)
        {
            if (deviceMapViewModel == null)
                throw new ArgumentNullException("DeviceMapViewModel");

            _deviceModels = DataAccess.Instance.GetDeviceModels();
            _deviceGraphModels = DataAccess.Instance.GetDeviceGraphModels();
            _deviceMapViewModel = deviceMapViewModel;
            _deviceViewModels = new ObservableCollection<DeviceBaseViewModel>();
            DataAccess.Instance.DeviceModelRemoveHandler += HandleDeviceModelRemove;
        }

        ~DeviceViewModelRepository()
        {
            DataAccess.Instance.DeviceModelRemoveHandler -= HandleDeviceModelRemove;
        }

        #endregion // Constructor

        #region Fields

        List<DeviceBaseModel> _deviceModels;
        List<DeviceBaseGraphModel> _deviceGraphModels;
        ObservableCollection<DeviceBaseViewModel> _deviceViewModels;
        DeviceMapViewModel _deviceMapViewModel;

        static Dictionary<DeviceType, uint> _deviceIDPreFix = new Dictionary<DeviceType, uint>
        {
            {DeviceType.Axle, 0x04000000},
            {DeviceType.AxleName, 0x31000000},
            {DeviceType.Button, 0x04000000},
            {DeviceType.CBI, 0x33000000},
            {DeviceType.ControlPoint, 0x00000000},
            {DeviceType.DynamicBeacon, 0x07000000},
            {DeviceType.GradePoint, 0x06000000},
            {DeviceType.MiscellaneousDevice, 0x04000000},
            {DeviceType.Path, 0x20000000},
            {DeviceType.Platform, 0x04000000},
            {DeviceType.PSD, 0x04000000},
            {DeviceType.Route, 0x18000000},
            {DeviceType.Signal, 0x10000000},
            {DeviceType.StaticBeacon, 0x1A000000},
            {DeviceType.StopPoint, 0x05000000},
            {DeviceType.Switch, 0x08000000},
            {DeviceType.Track, 0x1C000000},
            {DeviceType.TrackEnd, 0x32000000},
            {DeviceType.TrackSegment, 0x19000000},
            {DeviceType.ZoneController, 0x01000000},
            {DeviceType.Unknow, 0xFF000000}
        };

        #endregion // Fields

        #region Properties

        public ObservableCollection<DeviceBaseViewModel> DeviceViewModels
        {
            get
            {
                return _deviceViewModels;
            }
        }

        #endregion // Properties

        #region Events

        public event EventHandler<DeviceViewModelEventArgs> DeviceAdd;
        public event EventHandler<DeviceViewModelEventArgs> DeviceRemove;

        #endregion // Events

        #region Methods

        public void Init()
        {
            foreach (DeviceBaseGraphModel deviceGraphModel in _deviceGraphModels.Where(v => v.GraphType == _deviceMapViewModel.DisplayName))
            {
                DeviceBaseModel deviceModel = _deviceModels.FirstOrDefault(v1 => v1.DeviceID == deviceGraphModel.DeviceID);
                if (deviceModel != null)
                {
                    List<DeviceBaseViewModel> deviceViewModels = DeviceViewModelFactory.GenerateDeviceViewModel(deviceModel, deviceGraphModel);

                    foreach (DeviceBaseViewModel deviceViewModel in deviceViewModels)
                    {
                        deviceViewModel.DeviceMapViewModel = _deviceMapViewModel;
                        this._deviceViewModels.Add(deviceViewModel);
                        this.DeviceAdd(this, new DeviceViewModelEventArgs(deviceViewModel));
                    }   
                }
            }
        }

        public List<DeviceBaseViewModel> GetDeviceViewModels()
        {
            return new List<DeviceBaseViewModel>(_deviceViewModels);
        }

        public void Clear()
        {
            foreach (DeviceBaseViewModel deviceViewModel in this.GetDeviceViewModels())
            {
                this.Remove(deviceViewModel);
            }
        }

        public bool Add(DeviceBaseViewModel deviceViewModel)
        {
            if (Contains(deviceViewModel))
            {
                return true;
            }

            if (!
                (
                    (
                    deviceViewModel is TrackSegmentViewModel &&
                    ((TrackSegmentViewModel)deviceViewModel).TSViewModelType == TrackSegmentViewModelType.End
                    ) ||
                    (
                    deviceViewModel is SwitchViewModel &&
                    (
                    ((SwitchViewModel)deviceViewModel).SwViewModelType == SwitchViewModelType.Normal ||
                    ((SwitchViewModel)deviceViewModel).SwViewModelType == SwitchViewModelType.Reverse
                    )
                    )
                )
                )
            {
                if (_deviceGraphModels.Contains(deviceViewModel.DeviceGraphModel))
                {
                    return false;
                }

                // 向主视图添加设备的时候当前应该无此设备
                // 向其他设备添加设备图形的时候应该已有此设备
                if (_deviceMapViewModel.IsMainMap)
                {
                    if (_deviceModels.Select(v => v.DeviceID).Contains(deviceViewModel.DeviceModel.DeviceID) || !IsDeviceIDAvailable(deviceViewModel))
                    {
                        if (!AssignDeviceID(deviceViewModel))
                        {
                            return false;
                        }
                    }

                    _deviceModels.Add(deviceViewModel.DeviceModel);
                    DataAccess.Instance.Add(deviceViewModel.DeviceModel);
                }
                else
                {
                    if (!_deviceModels.Select(v => v.DeviceID).Contains(deviceViewModel.DeviceModel.DeviceID))
                    {
                        return false;
                    }
                }

                _deviceGraphModels.Add(deviceViewModel.DeviceGraphModel);
                DataAccess.Instance.Add(deviceViewModel.DeviceGraphModel);
            }

            deviceViewModel.DeviceMapViewModel = _deviceMapViewModel;
            _deviceViewModels.Add(deviceViewModel);
            this.DeviceAdd(this, new DeviceViewModelEventArgs(deviceViewModel));

            return true;
        }

        public bool Remove(DeviceBaseViewModel deviceViewModel)
        {
            if (!_deviceViewModels.Contains(deviceViewModel))
            {
                return false;
            }

            _deviceViewModels.Remove(deviceViewModel);
            this.DeviceRemove(this, new DeviceViewModelEventArgs(deviceViewModel));

            if (!
                (
                    (
                    deviceViewModel is TrackSegmentViewModel &&
                    ((TrackSegmentViewModel)deviceViewModel).TSViewModelType == TrackSegmentViewModelType.End
                    ) ||
                    (
                    deviceViewModel is SwitchViewModel &&
                    (
                    ((SwitchViewModel)deviceViewModel).SwViewModelType == SwitchViewModelType.Normal ||
                    ((SwitchViewModel)deviceViewModel).SwViewModelType == SwitchViewModelType.Reverse
                    )
                    )
                )
                )
            {
                _deviceGraphModels.Remove(deviceViewModel.DeviceGraphModel);
                DataAccess.Instance.Remove(deviceViewModel.DeviceGraphModel);

                if (_deviceMapViewModel.IsMainMap)
                {
                    _deviceModels.Remove(deviceViewModel.DeviceModel);
                    DataAccess.Instance.Remove(deviceViewModel.DeviceModel);

                    
                }
            }

            return true;
        }

        public bool Contains(DeviceBaseViewModel device)
        {
            if (device == null)
                throw new ArgumentNullException("Device");

            return _deviceViewModels.Contains(device);
        }

        private void HandleDeviceModelRemove(object sender, DeviceModelEventArgs e)
        {
            List<DeviceBaseViewModel> deviceViewModels = new List<DeviceBaseViewModel>(_deviceViewModels.Where(v => v.DeviceModel == e.DeviceModel));

            foreach (DeviceBaseViewModel delDeviceViewModel in deviceViewModels)
            {
                if (this.Remove(delDeviceViewModel))
                {
                    _deviceMapViewModel.DeviceMapRepository.MainDeviceMapViewModel.UndoRedo.InsertInUnDoRedoForDelete(delDeviceViewModel);
                }
            }
        }

        private bool IsDeviceIDAvailable(DeviceBaseViewModel deviceViewModel)
        {
            uint uiDeviceID = (uint)deviceViewModel.DeviceModel.DeviceID;

            // ID与类型相符
            if (uiDeviceID < _deviceIDPreFix[deviceViewModel.DeviceType] || uiDeviceID > (_deviceIDPreFix[deviceViewModel.DeviceType] + 0x00FFFFFF))
            {
                return false;
            }

            return true;
        }

        private bool AssignDeviceID(DeviceBaseViewModel deviceViewModel)
        {
            for (uint i = _deviceIDPreFix[deviceViewModel.DeviceType]; i <= (_deviceIDPreFix[deviceViewModel.DeviceType] + 0x00FFFFFF); i++)
            {
                if (!_deviceModels.Select(v => v.DeviceID).Contains((int)i))
                {
                    deviceViewModel.DeviceModel.DeviceID = (int)i;
                    deviceViewModel.DeviceGraphModel.DeviceID = (int)i;
                    return true;
                }
            }

            return false;
        }

        public XElement GetXMLSerializedData()
        {
            XElement root = new XElement(_deviceMapViewModel.DisplayName);

            foreach (DeviceBaseViewModel deviceViewModel in _deviceViewModels)
            {
                root.Add(Utilities.SerializeDeviceViewModel(deviceViewModel));
            }

            return root;
        }

        public void ApplyChanges(XElement xmlChanges)
        {
            foreach(var e in xmlChanges.Elements())
            {
                string deviceID = (string)e.Attribute("DeviceID");
                DeviceBaseViewModel deviceViewModel = _deviceViewModels.FirstOrDefault(v => Utilities.CompareID(deviceID, v.DeviceID));

                if (deviceViewModel != null)
                {
                    Type deviceViewModelType = deviceViewModel.GetType();

                    foreach (var el in e.Elements())
                    {
                        PropertyInfo propertyInfo = deviceViewModelType.GetProperty(el.Name.LocalName);
                        string propertyStringValue = (string)el;

                        if (propertyInfo != null)
                        {
                            Type propertyType = propertyInfo.PropertyType;

                            try
                            {
                                if (propertyType == typeof(string))
                                {
                                    propertyInfo.SetValue(deviceViewModel, propertyStringValue, null);
                                }
                                else if (propertyType == typeof(double))
                                {
                                    propertyInfo.SetValue(deviceViewModel, double.Parse(propertyStringValue), null);
                                }
                                else if (propertyType == typeof(bool))
                                {
                                    propertyInfo.SetValue(deviceViewModel, bool.Parse(propertyStringValue), null);
                                }
                                else if (propertyType == typeof(Point))
                                {
                                    propertyInfo.SetValue(deviceViewModel, Point.Parse(propertyStringValue), null);
                                }
                                else if (propertyType == typeof(int))
                                {
                                    propertyInfo.SetValue(deviceViewModel, int.Parse(propertyStringValue), null);
                                }
                            }
                            catch (System.Exception ex)
                            {

                            }
                        }
                    }
                }
            }
        }

        #endregion // Methods
    }
}
